package com.sxhuayuan.parking.service.impl;

import java.util.Calendar;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.sxhuayuan.parking.compenent.Message;
import com.sxhuayuan.parking.config.CarActType;
import com.sxhuayuan.parking.config.PayMode;
import com.sxhuayuan.parking.config.Platform;
import com.sxhuayuan.parking.controller.open.req.ParkInNoticeData;
import com.sxhuayuan.parking.controller.open.req.ParkOutNoticeData;
import com.sxhuayuan.parking.dto.ParkFeeCacheBean;
import com.sxhuayuan.parking.entity.Member;
import com.sxhuayuan.parking.entity.Order;
import com.sxhuayuan.parking.entity.ParkChannel;
import com.sxhuayuan.parking.entity.ParkingIn;
import com.sxhuayuan.parking.entity.ParkingLot;
import com.sxhuayuan.parking.entity.Partnerpay;
import com.sxhuayuan.parking.exception.ParkException;
import com.sxhuayuan.parking.plugin.parking.ParkingPlugin;
import com.sxhuayuan.parking.plugin.parking.result.QueryParkingFeeResultWrap;
import com.sxhuayuan.parking.plugin.partnerpay.PartnerpayApiService;
import com.sxhuayuan.parking.plugin.partnerpay.result.NoticeInResult;
import com.sxhuayuan.parking.plugin.partnerpay.result.QueryStateResult;
import com.sxhuayuan.parking.service.ExitingCarService;
import com.sxhuayuan.parking.service.MemberService;
import com.sxhuayuan.parking.service.OrderService;
import com.sxhuayuan.parking.service.ParkChannelService;
import com.sxhuayuan.parking.service.ParkFeeCacheService;
import com.sxhuayuan.parking.service.ParkService;
import com.sxhuayuan.parking.service.ParkingInService;
import com.sxhuayuan.parking.service.ParkingLotService;
import com.sxhuayuan.parking.service.PartnerpayService;
import com.sxhuayuan.parking.service.PluginService;
import com.sxhuayuan.parking.service.RedisService;
import com.sxhuayuan.parking.service.impl.pojo.ExitingCar;
import com.sxhuayuan.parking.service.impl.pojo.ParkInNoticeResult;
import com.sxhuayuan.parking.service.impl.pojo.ParkPreOutNoticeResult;
import com.sxhuayuan.parking.utils.DateUtils;
import com.sxhuayuan.parking.utils.DeviceUtil;

@Component("parkServiceImpl")
public class ParkServiceImpl implements ParkService {

	Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	RedisService redisService;
	@Autowired
	ParkingInService inService;
	@Autowired
	PartnerpayService partnerpayService;
	@Autowired
	ParkChannelService exitService;
	@Autowired
	ParkFeeCacheService parkFeeCacheService;
	@Autowired
	OrderService orderService;
	@Autowired
	PluginService pluginService;
	@Autowired
	ExitingCarService exitingCarService;
	@Autowired
	ParkingLotService lotService;
	@Autowired
	MemberService memberService;

	@Override
	public ParkPreOutNoticeResult preOutNotice(ParkingLot lot, ParkOutNoticeData data) throws ParkException {
		return preOutNotice(lot, data, true, true);
	}

	@Override
	public ParkPreOutNoticeResult preOutNotice(ParkingLot lot, ParkOutNoticeData data, boolean isFree, boolean isFreeLeav) throws ParkException {
		// 并发问题
		String carPlate = data.getCarPlate();
		if (!redisService.saveString(carPlate, carPlate, 1)) {
			throw new ParkException(Message.COMMON_REQUEST_FREQUENTLY);
		}
		ParkingIn in = inService.findByCarPlate(lot.getId(), carPlate);
		if (in == null) {
			throw new ParkException(Message.CAR_NO_ENTRANCE_RECORD);
		}
		ParkChannel exit = null;
		if (!StringUtils.isEmpty(data.getChannel())) {
			exit = exitService.findBySn(data.getChannel());
		}
		if (exit == null && !StringUtils.isEmpty(data.getDevice())) {
			exit = exitService.findByDevice(lot.getId(), data.getDevice());
		}
		if (exit == null) {
			return ParkPreOutNoticeResult.error(Message.PARK_CHANNEL_NOT_EXISTS);
		}
		log.debug("[{}]出场时间={}", carPlate, data.getTime());
		Date outTime = DateUtils.parse(data.getTime(), DateUtils.YYYYMMDDHHMMSS);
		if (isFree && in.getLastChargeTime() == null) {
			// 未缴过费，判断是否是免费时长内
			if (DateUtils.diff(in.getInTime(), outTime, Calendar.MINUTE) <= data.getFreeMinutes()) {
				log.debug("[{}]未超过免费停车时间:{}", carPlate, data.getFreeMinutes());
				return ParkPreOutNoticeResult.success("免费停车时间内");
			}
		} else {
			if (isFreeLeav && DateUtils.diff(in.getLastChargeTime(), outTime, Calendar.MINUTE) <= data.getFreeMinutesForLeaving()) {
				log.debug("[{}]未超过免费离场时间:{}", carPlate, data.getFreeMinutesForLeaving());
				return ParkPreOutNoticeResult.success("免费离场时间内");
			}
		}
		// 可能出场通知中没有上传金额，需要后台去车场接口中算费
		if (data.getFee() == null) {
			ParkingPlugin parkingPlugin = pluginService.getParkingPlugin(lot.getPluginId());
			QueryParkingFeeResultWrap parkingFeeResultWrap = parkingPlugin.queryParkingFee(lot, in.getCarPlate());
			if (parkingFeeResultWrap == null || !parkingFeeResultWrap.getIsSuccess()) {
				log.error("[{}]查询车费失败, 稍候重试", carPlate);
				return ParkPreOutNoticeResult.error(Message.API_QUERY_FEE_FAIL);
			}
			Float fee = parkingFeeResultWrap.getCalcFee();
			data.setFee(fee);
		}
		ParkFeeCacheBean parkFee = new ParkFeeCacheBean();
		parkFee.setDiscountFee(data.getDiscount());
		parkFee.setEndTime(outTime);
		parkFee.setExitDevice(exit.getDeviceId());
		parkFee.setParkPluginId(lot.getPluginId());
		parkFee.setUniqueId(in.getUniqueId());
		parkFee.setTotalFee(data.getFee() + data.getDiscount());

		ParkPreOutNoticeResult rs = new ParkPreOutNoticeResult();
		rs.setUniqueId(in.getUniqueId());
		String errmsg = null;

		try {
			if (lot.getIsPartnerpayEnable()) {
				/*
				 * 车辆入场时会尝试在平台进行入场，入场成功时platform字段会被设置，表示车辆已在此平台入场。为空则表示所有平台无感均未开通。
				 * 出场时如果发现未入场要重新尝试调用微信/支付宝入场接口，更新用户无感状态，因为用户有可能在入场后开通了无感权限。
				 */
				if (in.getPlatform() == null) {
					// 这里不抛异常则肯定入场成功了，下面无须再判断用户状态
					in = partnerpayEnter(in);
				}

				final String ip = DeviceUtil.getInternetIp();
				try {
					Member member = memberService.findOne(in.getMemberId());
					Order order = orderService.create(in, parkFee, member, in.getPlatform(), PayMode.partnerpay, Order.Method.partnerpay, ip, "");
					errmsg = "已发起无感支付, 订单:" + order.getSn();
					rs.setIsPassable(true);
				} catch (Exception e) {
					log.error("保存失败: ", e);
				}
			} else {
				// 是否已支付

				if (in.getLastChargeTime() != null && DateUtils.diff(in.getLastChargeTime(), outTime, Calendar.MINUTE) < data.getFreeMinutesForLeaving()) {
					log.info("[{}]已扫码缴费{}，放行", carPlate, DateUtils.format(in.getLastChargeTime()));
					rs.setMsg("已扫码缴费");
					rs.setIsPassable(true);
					return rs;
				}

				errmsg = Message.CAR_FEE_NOT_PAID;
				rs.setIsPassable(false);
			}
		} catch (ParkException e) {
			throw e;
		} finally {
			if (!rs.getIsPassable()) {
				log.debug("保存出口车牌, {} , {}", exit.getSn(), carPlate);
				exitingCarService.put(exit.getSn(), new ExitingCar(lot.getSn(), exit.getId(), exit.getDeviceId(), carPlate, data.getFee(), data.getDiscount(),
						DateUtils.format(in.getInTime()), DateUtils.format(outTime), data.getParkMinutes(), data.getLotOrderNo(), data.getLotRecordId(), in.getUniqueId()));
				log.info("保存费用park.fee: {}", data.getCarPlate());
				parkFeeCacheService.put(parkFee);
			}
		}
		log.debug("[{}]{}", data.getCarPlate(), errmsg);
		rs.setMsg(errmsg);
		return rs;
	}

	@Override
	public ParkingIn partnerpayEnter(ParkingIn in) throws ParkException {
		Boolean isSuccess = false;
		ParkingLot lot = lotService.findById(in.getParkingLotId());
		// 查询优先使用的通道，首先是车场配置，其次是平台配置
		Platform[] platforms = null;
		try {
			platforms = lotService.findPreferentialPartnerpayPlatform(lot);
		} catch (Exception e) {
			platforms = Platform.values();
		}

		if (platforms == null) {
			log.error("[{}]没用可用的无感通道", in.getCarPlate());
			throw new ParkException(Message.PARTNERPAY_NO_AVAILABLE_PLATFORM);
		}
		for (Platform platform : platforms) {
			log.debug("[{}]platform={}", in.getCarPlate(), platform);

			// 调用入场接口通知平台
			PartnerpayApiService partnerpayApiService = pluginService.getPartnerpayApiService(platform);
			if (partnerpayApiService == null || !partnerpayApiService.isEnabled()) {
				log.error("[{}]{} partnerpayApiService is null!", in.getCarPlate(), platform);
				continue;
			}

			QueryStateResult queryStateResult = partnerpayApiService.queryUserState(lot, null, null, in.getCarPlate(), null);

			if (queryStateResult != null && !queryStateResult.isUserStateNormal()) {
				log.info("[{}]用户状态不可用: {}", in.getCarPlate(), queryStateResult.getUserState());
				continue;
			}

			String openid = queryStateResult.getOpenid();
			Member member = memberService.findByOpenId(openid);
			if (member == null) {
				member = new Member();
				member.setOpenid(openid);
				member.setAppid(queryStateResult.getAppid());
				member.setPlatform(platform);
				memberService.save(member);
			}
			// String subOpenid = queryStateResult.getSubOpenid();
			NoticeInResult noticeInResult = partnerpayApiService.noticeIn(in);
			if (noticeInResult != null) {
				// 签约记录
				Partnerpay partner = partnerpayService.findByCarPlateAndPlatform(in.getCarPlate(), platform);
				if (noticeInResult.getIsResultSuccess()) {
					if (partner == null) {
						partner = new Partnerpay();
						partner.setCarPlate(in.getCarPlate());
						partner.setMember(member);
						partner.setPlatform(partnerpayApiService.getPlatform());
						partner.setState(Partnerpay.State.NORMAL);
						partnerpayService.save(partner);
						log.debug("[{}]保存签约状态为可用: {}, id={}", in.getCarPlate(), platform, partner.getId());
					} else {
						log.debug("[{}]更新签约状态为可用: {}, id={}", in.getCarPlate(), platform, partner.getId());
						partner.setState(Partnerpay.State.NORMAL);
						partnerpayService.update(partner);
					}
					in.setPlatform(platform);
					in.setMemberId(partner.getMember().getId());
					inService.update(in);
					isSuccess = true;
					break;
				} else {
					if (partner != null) {
						partner.setState(Partnerpay.State.valueOf(noticeInResult.getUserState()));
						partnerpayService.update(partner);
					}
					log.debug("[{}]调用入场api失败: {}, msg={}", in.getCarPlate(), platform, noticeInResult.getMsg());
				}
			} else {
				log.debug("[{}]调用入场api失败: {}, msg={}", in.getCarPlate(), platform, "网络中断，结果未知");
			}
		}
		if (isSuccess)
			return in;
		else
			throw new ParkException(Message.PARTNERPAY_NO_AVAILABLE_PLATFORM);
	}

	@Override
	public ParkInNoticeResult parkInNotice(ParkingLot lot, ParkInNoticeData data) throws ParkException {
		ParkChannel channel = null;
		if (!StringUtils.isEmpty(data.getChannel())) {
			channel = exitService.findBySn(data.getChannel());
		} else if (!StringUtils.isEmpty(data.getDevice())) {
			channel = exitService.findByDevice(lot.getId(), data.getDevice());
		}
		Long channelId = channel != null ? channel.getId() : null;
		ParkingIn in = inService.enter(lot, data.getCarPlate(), DateUtils.parse(data.getTime(), DateUtils.YYYYMMDDHHMMSS), channelId, data.getFreeMinutes(),
				CarActType.find(data.getCarActType()));
		ParkInNoticeResult rs = new ParkInNoticeResult();
		rs.setUniqueId(String.valueOf(in.getUniqueId()));
		rs.setCarPlate(in.getCarPlate());
		return rs;
	}
}
